package TokenRecognizer;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author jefoutta
 */
import java.util.*;

public class DirectAFD {
    private Node<NodeExp> tree;
    private Integer countPos;
    private Integer pointer;
    private String re;
    private Hashtable<Integer,Sets<Integer>> nextPos = new Hashtable();
    private Sets<Integer> nextPosList = new Sets();
    private Hashtable<Integer, Character> leaves = new Hashtable();
    private Sets<Character> characters = new Sets();
    private Sets<Sets<Integer>> posStates = new Sets();
    private Integer endPos;
    private Integer index = 0;
    private Automata tautomata = new Automata();

    public DirectAFD(String tre){
        re = "."+tre + "#";
        pointer = 0;
        countPos = 1;
    }

    public Node<NodeExp> basic(char c){
        NodeExp temp = new NodeExp(c);
        Node<NodeExp> leave = new Node(temp);
        if(leave.getElement().getLabel() == 'e')
            leave.getElement().setNullable(true);
        else
            leave.getElement().setNullable(false);
        leave.getElement().setPos(countPos);
        leave.getElement().setFirstPos(countPos);
        leave.getElement().setLastPos(countPos);
        leaves.put(countPos, c);
        if(!characters.contains(c)&&(c != '#'))
            characters.add(c);
        else
            if(c == '#'){
                endPos = countPos;
            }
        countPos++;
        
        return leave;
    }

    public Node<NodeExp> or(Node<NodeExp> n1, Node<NodeExp> n2, char c){
        NodeExp temp = new NodeExp(c);
        Node<NodeExp> leaf = new Node(temp);
        leaf.setLeftChild(n1);
        leaf.setRightChild(n2);
        leaf.getElement().setNullable((leaf.getLeftChild().getElement().getNullable())||(leaf.getRightChild().getElement().getNullable()));
        leaf.getElement().setFirstPos(leaf.getLeftChild().getElement().getFirstPosSet());
        leaf.getElement().setFirstPos(leaf.getRightChild().getElement().getFirstPosSet());
        leaf.getElement().setLastPos(leaf.getLeftChild().getElement().getLastPosSet());
        leaf.getElement().setLastPos(leaf.getRightChild().getElement().getLastPosSet());

        return leaf;
    }

    public Node<NodeExp> concat(Node<NodeExp> n1, Node<NodeExp> n2, char c){
        NodeExp temp = new NodeExp(c);
        Node<NodeExp> leaf = new Node(temp);
        leaf.setLeftChild(n1);
        leaf.setRightChild(n2);
        leaf.getElement().setNullable((leaf.getLeftChild().getElement().getNullable())&&(leaf.getRightChild().getElement().getNullable()));
        leaf.getElement().setFirstPos(leaf.getLeftChild().getElement().getFirstPosSet());
        if(leaf.getLeftChild().getElement().getNullable())
            leaf.getElement().setFirstPos(leaf.getRightChild().getElement().getFirstPosSet());
        leaf.getElement().setLastPos(leaf.getRightChild().getElement().getLastPosSet());
        if(leaf.getRightChild().getElement().getNullable())
            leaf.getElement().setLastPos(leaf.getLeftChild().getElement().getFirstPosSet());

        return leaf;
    }

    public Node<NodeExp> kleen(Node<NodeExp> n1, char c){
        NodeExp temp = new NodeExp(c);
        Node<NodeExp> leaf = new Node(temp);
        leaf.setCenterChild(n1);
        leaf.getElement().setNullable(true);
        leaf.getElement().setFirstPos(leaf.getCenterChild().getElement().getFirstPosSet());
        leaf.getElement().setLastPos(leaf.getCenterChild().getElement().getLastPosSet());

        return leaf;
    }

    public Node<NodeExp> getTree(){
        Node<NodeExp> temp = null;
        char c = c = re.charAt(pointer);
        if(pointer < re.length())
            switch(re.charAt(pointer)){
                case '|':{
                    pointer++;
                    temp = this.or(this.getTree(), this.getTree(), c);
                }break;
                case '*':{
                    pointer++;
                    temp = this.kleen(this.getTree(), c);
                }break;
                case '.':{
                    pointer++;
                    temp = this.concat(this.getTree(), this.getTree(), c);
                }break;
                default:{
                    pointer++;
                    temp = this.basic(c);                   
                }
            }
        return temp;
    }

    public void nexPos(Node<NodeExp> tnode){
        switch(tnode.getElement().getLabel()){
            case '*':{
                for(int i = 0; i < tnode.getElement().getLastPosSet().getSize(); i++){
                    if(!nextPosList.contains(tnode.getElement().getLastPosSet().get(i))){
                        nextPos.put(tnode.getElement().getLastPosSet().get(i), new Sets());
                        nextPosList.add(tnode.getElement().getLastPosSet().get(i));
                    }
                    nextPos.get(tnode.getElement().getLastPosSet().get(i)).addT(tnode.getElement().getFirstPosSet());
                }
            }break;
            case '.':{
                for(int i = 0; i < tnode.getLeftChild().getElement().getLastPosSet().getSize(); i++){
                    if(!nextPosList.contains(tnode.getLeftChild().getElement().getLastPosSet().get(i))){
                        nextPos.put(tnode.getLeftChild().getElement().getLastPosSet().get(i), new Sets());
                        nextPosList.add(tnode.getLeftChild().getElement().getLastPosSet().get(i));
                    }
                    nextPos.get(tnode.getLeftChild().getElement().getLastPosSet().get(i)).addT(tnode.getRightChild().getElement().getFirstPosSet());
                }
            }break;
        }
        if(tnode.getLeftChild() != null)
            this.nexPos(tnode.getLeftChild());
        if(tnode.getCenterChild() != null)
            this.nexPos(tnode.getCenterChild());
        if(tnode.getRightChild() != null)
            this.nexPos((tnode.getRightChild()));
    }

    public Automata getAFD(){
        tree = this.getTree();
        this.nexPos(tree);
        posStates.add(tree.getElement().getFirstPosSet());
        tautomata.addFirstState(new State());
        if(tree.getElement().getFirstPosSet().contains(endPos))
            tautomata.getFirstState().setAcceptation(true);
        do{
            this.move(posStates.get(index));
            index++;
        }while(index < posStates.getSize());
        tautomata.setDeterministic(true);
        return tautomata;
    }

    public void move(Sets<Integer> tset){
        Sets<Integer> temp = new Sets();
        State tstate = null;
        for(int i = 0; i < characters.getSize(); i++){
           temp = new Sets();
           for(int j = 0; j < tset.getSize(); j++)
               if(leaves.get(tset.get(j)) == characters.get(i))
                   temp.add(nextPos.get(tset.get(j)));
           if(!posStates.contains(temp)){
               posStates.add(temp);
               tstate = new State();
               tautomata.addLastState(tstate);
               if(temp.contains(endPos))
                   tautomata.getLastState().setAcceptation(true);
               tautomata.setTransition(tautomata.getState(index), tautomata.getLastState(), new Transition(characters.get(i)));
           }else{
               tautomata.setTransition(tautomata.getState(index), tautomata.getState(posStates.indexOf(temp)), new Transition(characters.get(i)));
           }
        }
    }

    public String toString(){
        String s = "";
        for(int i = 0; i < nextPosList.getSize(); i ++)
            s = (s + nextPosList.get(i)+": "+ nextPos.get(nextPosList.get(i))+ "\n");
        return s;
    }
}